﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
using System.ServiceProcess;
using System.Threading;

namespace Progetto
{
    public partial class Main_Form : Form
    {
        public Main_Form()
        {
            InitializeComponent();

            check_service(); // checks 'WlanService' status

            getNICsToolStripMenuItem_Click(null, null); // provides immediately information about NIC status

            // Places_Timer
            Places_timer = new System.Timers.Timer();
            Places_timer.Interval = 1000;

            updatedNetworkList = new Dictionary<string, UInt32>();

            rTf = new ReadTextFile("networks.txt");
            rCf = new ReadCfgFile("export.cfg");

            places = new List<Place>();

            rCf.populate(ref places, ref Places_listView);
            oldPlaces = Places_listView.Items.Count;
        }

        /* PRIVATE MEMBERS */

        // TIMERS
        private System.Timers.Timer Places_timer;

        // The list of the nearest available networks
        private Dictionary<string, UInt32> updatedNetworkList;

        ReadTextFile rTf;
        ReadCfgFile rCf;
        WriteTextFile wTf;
        
        // The number of old places (the initial Places_listView.Items.Count value)
        int oldPlaces;

        // The current place (name)
        public static string currentPlace;


        /* PUBLIC MEMBERS */

        // The structure of all places found
        public List<Place> places;

        /*************************************************************************/

        /* Menu dei comandi */
        /* Wifi */
        // Get NICs
        private void getNICsToolStripMenuItem_Click(object sender, EventArgs e) {
            
            NIC_listBox.Items.Clear();
            Status_listBox.Items.Clear();

            Dictionary<string, string> names = Wifi.getInterfaces();

            if (names.Count == 0) {
                MessageBox.Show("No network interface cards found in this laptop.", "Attention", MessageBoxButtons.OK);
                return;
            }

            Dictionary<string, string>.KeyCollection keyColl = names.Keys;

            foreach (string s in keyColl) {
                NIC_listBox.Items.Add(s);
            }
            
            Dictionary<string, string>.ValueCollection valueColl =
                names.Values;
            
            foreach (string s in valueColl) {
                Status_listBox.Items.Add(s);
            }

        }
        // Places Info...
        private void placesInfoToolStripMenuItem_Click(object sender, EventArgs e) {
            
            if (Places_listView.Items.Count == 0) {
                MessageBox.Show("No registered places.", "Attention", MessageBoxButtons.OK);
                return;
            }
            
            Statistics_Form st = new Statistics_Form(places, currentPlace, Places_listView);
            st.ShowDialog();

        }
        // Export places configurations
        private void exportPlacesConfigurationsToolStripMenuItem_Click(object sender, EventArgs e) {

            if (Places_listView.Items.Count == 0) {
                MessageBox.Show("No registered places.", "Attention", MessageBoxButtons.OK);
                return;
            }

            wTf = new WriteTextFile("export.cfg");

            string line;

            for (int i = 0; i < places.Count; i++) {
                // place name
                line = "p:" + places[i].getPlaceName();
                wTf.write(line);
                // duration
                line = "d:" + places[i].getTimeOfStay().ToString();
                wTf.write(line);
                // networks
                Dictionary<string, UInt32> nlist = places[i].getNetworkList();
                
                foreach (KeyValuePair<string, UInt32> rete in nlist) {
                    line = "n:" + rete.Key + ":" + rete.Value;
                    wTf.write(line);
                }
                // actions
                List<string> alist = places[i].getActionList();
                
                foreach (string act in alist) {
                    line = "a:" + act;
                    wTf.write(line);
                }

                if(i != places.Count - 1)
                    wTf.newLine();

            }
            wTf.close();

            MessageBox.Show("Places configurations exported.", "Info", MessageBoxButtons.OK);
        }
        // Exit
        private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
            this.Close();
        }

        /* About */
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
            About_Form about_form = new About_Form();
            about_form.ShowDialog();
        }

        /*************************************************************************/

        private void Wifi_Button_Click(object sender, EventArgs e) {
            
            Networks_listView.Items.Clear();

            Dictionary<string, UInt32> network_list = rTf.getNetworkList();

            if (network_list.Count == 0) {

                // [RICHEDIT] allineamento del testo al centro
                Info_Wifi_richTextBox.SelectionAlignment = HorizontalAlignment.Center;
                Info_Wifi_richTextBox.Text = "-- [" + DateTime.Now.ToString() + "] --\n" + network_list.Count + " networks found";
                Place_Button.Enabled = false;
                return;

            }
            else {
                // [RICHEDIT] allineamento del testo al centro
                Info_Wifi_richTextBox.SelectionAlignment = HorizontalAlignment.Center;
                if (network_list.Count == 1)
                    Info_Wifi_richTextBox.Text = "-- [" + DateTime.Now.ToString() + "] --\n" + network_list.Count + " network found";
                else
                    Info_Wifi_richTextBox.Text = "-- [" + DateTime.Now.ToString() + "] --\n" + network_list.Count + " networks found";
            }

            foreach(string networkName in network_list.Keys) {

                ListViewItem lv = new ListViewItem(networkName);
                lv.SubItems.Add(network_list[networkName].ToString() + " %");
                Networks_listView.Items.Add(lv);
                
            }

            // Update network list after retreiving new information (COPY CONSTRUCTOR)
            updatedNetworkList = new Dictionary<string, uint>(network_list);

            /* Wifi_timer: scansiona periodicamente le reti circostanti */
            if (!Wifi_timer.Enabled)
                Wifi_timer.Start();
            else {
                Wifi_timer.Stop();
                Wifi_timer.Start();
            }

            if(Place_Button.Enabled == false)
                Place_Button.Enabled = true;

            if(CurrentPlace_button.Enabled == false)
                CurrentPlace_button.Enabled = true;
        }
        
        private void Clear_Button_Click(object sender, EventArgs e) {

            Info_Wifi_richTextBox.Clear();
            Networks_listView.Items.Clear();
            Place_Button.Enabled = false;

            if (Wifi_timer.Enabled)
                Wifi_timer.Stop();

        }

        private void Wifi_timer_Tick(object sender, EventArgs e) {

            Networks_listView.Items.Clear();

            Dictionary<string, UInt32> network_list = rTf.getNetworkList();

            if (network_list.Count == 0)
            {

                // [RICHEDIT] allineamento del testo al centro
                Info_Wifi_richTextBox.SelectionAlignment = HorizontalAlignment.Center;
                Info_Wifi_richTextBox.Text = "-- [" + DateTime.Now.ToString() + "] --\n" + network_list.Count + " networks found";
                Place_Button.Enabled = false;
                return;

            }
            else
            {
                // [RICHEDIT] allineamento del testo al centro
                Info_Wifi_richTextBox.SelectionAlignment = HorizontalAlignment.Center;
                if (network_list.Count == 1)
                    Info_Wifi_richTextBox.Text = "-- [" + DateTime.Now.ToString() + "] --\n" + network_list.Count + " network found";
                else
                    Info_Wifi_richTextBox.Text = "-- [" + DateTime.Now.ToString() + "] --\n" + network_list.Count + " networks found";
            }

            foreach (string networkName in network_list.Keys)
            {

                ListViewItem lv = new ListViewItem(networkName);
                lv.SubItems.Add(network_list[networkName].ToString() + " %");
                Networks_listView.Items.Add(lv);

            }

            // Update network list after retreiving new information (COPY CONSTRUCTOR)
            updatedNetworkList = new Dictionary<string, uint>(network_list);

            if (Place_Button.Enabled == false)
                Place_Button.Enabled = true;

        }

        private void Place_Button_Click(object sender, EventArgs e) {
            
            // Dialog box for place name insertion
            NewPlace_Form dialog = new NewPlace_Form();

            if (places.Count == 0) {
                
                if (dialog.ShowDialog(this) == DialogResult.OK) {
                    string placeName = dialog.insertedText;
                    
                    // Create a new Place object
                    Place p = new Place(placeName);
                    p.setNetworkList(updatedNetworkList);
                    places.Add(p);
                    
                    // Display on ListViewControl
                    ListViewItem lv = new ListViewItem(Convert.ToString(places.Count - oldPlaces));
                    lv.SubItems.Add(placeName);
                    lv.SubItems.Add(p.getTimeOfStay().ToString());
                    Places_listView.Items.Add(lv);
                    Places_listView.EnsureVisible(lv.Index);
                    lv.Selected = true;

                    // Set the current place name
                    currentPlace = placeName;

                    Place_timer.Enabled = true;

                }
                else
                    return;
            }
            // places.Count > 0
            else {
                if (dialog.ShowDialog(this) == DialogResult.OK) {
                    string placeName = dialog.insertedText;

                    for (int i = 0; i < places.Count; i++) {
                        if (placeName.ToLower() == places[i].getPlaceName().ToLower()) {
                            MessageBox.Show("Place name already present in the database.", "Attention", MessageBoxButtons.OK);
                            return;
                        }
                    }
                    // Verify that the place we are going to add has not the same "position" of another place
                    if(IsSimilarPosition()) {
                        MessageBox.Show("You are in a registered place. Click on 'Where am I' button for more information.", "Attention", MessageBoxButtons.OK);
                        return;
                    }
                    
                    /* NEW PLACE */
                    // Create a new Place object
                    Place p = new Place(placeName);
                    p.setNetworkList(updatedNetworkList);
                    places.Add(p);

                    // Display on ListViewControl
                    ListViewItem lv = new ListViewItem(Convert.ToString(places.Count - oldPlaces));
                    lv.SubItems.Add(placeName);
                    lv.SubItems.Add(p.getTimeOfStay().ToString());
                    Places_listView.Items.Add(lv);
                    Places_listView.EnsureVisible(lv.Index);
                    lv.Selected = true;

                    // Set the current place name (overwrite)
                    currentPlace = placeName;

                    Place_timer.Enabled = true;

                }
                else
                    return;       
            }
        }

        private void Place_timer_Tick(object sender, EventArgs e) {
            for(int i = 0; i < places.Count; i++) {
                if(places[i].getPlaceName() == currentPlace) {
                    places[i].upgradeTimeofStay();
                    for(int j = 0; j < Places_listView.Items.Count; j++) {
                        ListViewItem lv = Places_listView.Items[j];
                        if(lv.SubItems[1].Text.Equals(currentPlace)) {
                            lv.SubItems[2].Text = places[i].getTimeOfStay().ToString();
                        }
                    }
                }
            }
        }

        private void CurrentPlace_button_Click(object sender, EventArgs e) {
            
            // lista nome posto - conteggio di reti uguali alle updatedNetwork
	        Dictionary<string, UInt32> list = new Dictionary<string,UInt32>();

	        //contatore del numero di reti uguali (aggiornate) al posto iesimo
	        UInt32 count;

	        if (places.Count != 0) {
		
		        for (int i = 0; i < places.Count; i++) {

			        count = 0;
			        Dictionary<string, UInt32> iNetworkList = places[i].getNetworkList();
			        foreach (string sKey in updatedNetworkList.Keys) {
				        if (iNetworkList.ContainsKey(sKey)) {
					        count++;
				        }
			        }

			        list.Add(places[i].getPlaceName(), count);
		        } //fine for places
	
		        uint max = list.Max(kvp => kvp.Value);
		        string pName = list.Where(kvp => kvp.Value == max).Select(kvp => kvp.Key).First();

		        string msg;
		        float percent = (float)max / (float)updatedNetworkList.Count;

		        if (max != 0) {
			        msg = "You might be in: '" + pName + "' (" + (percent * 100) + "% accuracy) ";
		        }
		        else {
			        msg = "You are not in a registered place.";
		        }
		        MessageBox.Show(msg, "Where am I?", MessageBoxButtons.OK);

	        }
	        else {
		        MessageBox.Show("There are no registered places.", "Where am I?", MessageBoxButtons.OK);
	        }
        }


        // Other methods
        private bool IsSimilarPosition() {

            // Retreive the updated network list and compare it with ALL other lists
            Dictionary<string, UInt32> list = new Dictionary<string, UInt32>(updatedNetworkList);
            int count = 0;
            int same_network = 0;

            for (int i = 0; i < places.Count; i++) {
                same_network = 0;
                Dictionary<string, UInt32> iNetworkList = places[i].getNetworkList();

                foreach (string sKey in list.Keys) {
                    if (iNetworkList.ContainsKey(sKey)) {
                        same_network++;

                        // +/- 20%
                        if (
                            (list[sKey] > iNetworkList[sKey] + 20)
                            ||
                            // networks lower than 20% are too weak to give useful information
                            (iNetworkList[sKey] > 20 && list[sKey] < iNetworkList[sKey] - 20)
                            )
                            count++;
                    }
                }


                if (same_network == 0 || same_network < list.Count / 2) {
                    continue;
                }
                else {
                    if (count > list.Count / 2) {
                        continue;
                    }
                    else {
                        return true;
                    }
                }

            }

            return false;
        }

        private void check_service() {
            ServiceController[] scServices;
            scServices = ServiceController.GetServices(); // retreive all services

            List<string> serviceNames = new List<string>();
            foreach (ServiceController service in scServices) {
                serviceNames.Add(service.ServiceName);
            }

            if (!serviceNames.Contains("WlanService")) {
                MessageBox.Show("WlanService not installed. Please install this service before running the application.", "Attention", MessageBoxButtons.OK);
                Environment.Exit(0);
            }

            foreach (ServiceController service in scServices) {
                if (service.ServiceName == "WlanService") {

                    float seconds = 0;
                    ServiceController sc = new ServiceController("WlanService");
                    if (sc.Status == ServiceControllerStatus.Stopped) {
                        sc.Start();
                        new Thread(new ThreadStart(delegate {
                                    MessageBox.Show
                                    (
                                    "Waiting for the service to start...", 
                                    "Message",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Warning
                                    );
                        })).Start();
                        while (sc.Status != ServiceControllerStatus.Running) {
                            sc.Refresh();
                            Thread.Sleep(1500);
                            seconds += 1.5f;
                            if(seconds >= 10) {
                                MessageBox.Show("WlanService could not start. Please check configuration service before running the application.", "Attention", MessageBoxButtons.OK);
                                Environment.Exit(0);
                            }
                        }
                        // waiting for the service to create the "networks.txt" file
                        Thread.Sleep(5000);
                    }

                }
            }
        }

        

        
    }
}